<?php
// $Id: FormElement.php 27 2010-11-16 12:49:54Z mjohnson60@gmail.com $
//
// $Author: mjohnson60@gmail.com $
// $Date: 2010-10-29 07:27:52 -0400 (Fri, 29 Oct 2010) $
//
// $Rev: 16 $
//


/**
 * Enter description here ...
 * 
 * @author markjohnson
 * @version 1.0
 * @copyright Copyright (c) 2010, markjohnson
 * @license http://www.gnu.org/licenses/gpl.html The GNU General Public License v3.0
 *
 */
abstract class FormElement extends Object
{
	var $type = '';
	var $input = TRUE;
	
	var $id = '';
	var $name = '';
	var $attributes = array();
	var $required = FALSE;
	var $value = '';
	
	var $title = '';
	
	var $children = array();
	var $parent = NULL;
	var $form = NULL;
	
	var $render = '';
	
	var $description = '';
	var $prefix = '';
	var $suffix = '';
	
	var $weight = 0;
	
	var $processed = FALSE;
	var $printed = FALSE;
	var $rendered = FALSE;
	var $sorted = FALSE;
	
	var $content = '';

	/**
	 * Enter description here ...
	 * 
	 * @param array $input
	 * @param string $name
	 * @param string $value
	 * @param string $id
	 * @param FormElement $parent
	 * @param array $attributes
	 */
	public function __construct($input = NULL, $name = NULL, $value = '', $id = '', $parent = NULL, $attributes = array())
	{
		if (empty($this->render))
		{
			$this->render = $this->type;
		}
		
		if (!empty($name))
		{
			$this->name = $name;
		}
		
		if (!empty($value))
		{
			$this->value = $value;
		}
		
		if (!empty($id))
		{
			$this->id = $id;
		}
		
		if (!empty($parent))
		{
			$this->parent = $parent;
		}
		
		if (!empty($attributes))
		{
			$this->attributes = $attributes;
		}
		
		if (!empty($input))
		{
			$children = element_children($input);
			if (!empty($input['#children']))
			{
				$input['#children'] = array_merge($input['#children'], $children);
			}
			else
			{
				$input['#children'] = $children;
			}
			
			foreach ($input as $key => $value)
			{
				$property = ($key[0] == '#');
				if ($property)
				{
					$key = substr($key, 1);
				}
				
				if ($property && $key == 'children')
				{
					foreach ($value as $child_name => $child)
					{
						if (is_string($child))
						{
							$child_name = $child;
							$child = $input[$child_name];
						}
						
						$element_child = FormElement::createElement($child, $child_name, $this);
						
						if ($element_child !== FALSE)
						{
							$this->addChild($element_child);
						}
					}
				}
				else if (property_exists($this, $key))
				{
					$this->{$key} = $value;
				}
			}
		}
	}

	/**
	 * setForm()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $form
	 */
	public function setForm($form)
	{
		//		var_dump($this);
		$this->form = $form;
		
		if (!empty($this->children))
		{
			foreach ($this->children as $child)
			{
				$child->setForm($form);
			}
		}
	}

	/**
	 * Enter description here ...
	 * 
	 * @param unknown_type $element
	 * @param unknown_type $parent
	 * @return boolean|Ambigous <NULL, CheckboxInput>
	 */
	public static function createElement($element, $name = '', $parent = NULL)
	{
		if (empty($element['#type']))
		{
			switch ($parent->type)
			{
				case 'radioset':
					$element['#type'] = 'radio';
					$element['#name'] = $parent->name;
					break;
				case 'select':
					$element['#type'] = 'option';
					break;
				default:
					return FALSE;
					break;
			}
		}
		
		$type = $element['#type'];
		
		$input = FALSE;
		$name = (!empty($element['#name']) ? $element['#name'] : (!empty($name) ? $name : ''));
		
		switch ($type)
		{
			case 'hidden':
				$input = new HiddenInput($name, $element);
				
				break;
			case 'textfield':
				$input = new TextFieldInput($name, $element);
				
				break;
			case 'textarea':
				$input = new TextAreaInput($name, $element);
				
				break;
			case 'password':
				$input = new PasswordInput($name, $element);
				
				break;
			case 'select':
				$input = new SelectInput($name, $element);
				
				break;
			case 'option':
				$input = new SelectOptionInput($name, $element);
				
				break;
			case 'submit':
				$element['#button_type'] = 'submit';
			case 'button':
				if (empty($element['#button_type']) || $element['#button_type'] == 'submit')
				{
					$input = new SubmitInput($name, $element);
				}
				else
				{
					$input = new ButtonInput($name, $element);
				}
				
				break;
			case 'checkbox':
				$input = new CheckboxInput($name, $element);
				
				break;
			case 'radio':
				$input = new RadioInput($name, $element);
				
				break;
			case 'radioset':
				$input = new RadioSetInput($name, $element);
				
				break;
			case 'fieldset':
				$input = new FieldsetInput($name, $element);
				
				break;
			default:
				break;
		}
		
		$input->parent = $parent;
		
		return $input;
	}

	/**
	 * Enter description here ...
	 * 
	 * @param unknown_type $child
	 */
	public function addChild($child)
	{
		$this->children[] = $child;
	}

	/**
	 * Enter description here ...
	 * 
	 */
	public function prepare()
	{
		// check to see if any inputs need defaults filled, etc.
		if (!$this->processed)
		{
			$this->processed = TRUE;
		}
		
		// set every element's id
		if (empty($this->id))
		{
			$this->id = unique_id($this->name);
		}
		
		$this->sorted = true;
		
		// only gets called for forms because for every form $form['#inputs'] is where the form inputs are located
		if (!empty($this->children))
		{
			$count = 0;
			foreach ($this->children as $key => $input)
			{
				if (empty($input->processed))
				{
					$input->processed = true;
				}
				
				if (!$input->children)
				{
					if (empty($input->name))
					{
						$input->name = $key;
					}
				}
				
				if (empty($input->weight))
				{
					$input->weight = $count;
				}
				else
				{
					$this->sorted = FALSE;
				}
				
				$input->prepare();
				$count++;
			}
		}
	}

	/**
	 * Enter description here ...
	 * 
	 * @return NULL|string
	 */
	public function compile()
	{
		if (empty($this))
		{
			return NULL;
		}
		
		if (empty($this->processed) || !$this->processed)
		{
			$this->fill_properties();
		}
		
		$output = '';
		if (empty($this->content))
		{
			$this->content = '';
			
			// if an element has a render function but hasn't been rendered, then render it
			if (!empty($this->render) && !$this->rendered)
			{
				$this->rendered = TRUE;
				
				// element is a single element, like a button
				if (empty($this->children))
				{
					$this->printed = TRUE;
					$this->content = $this->render();
					$output = $this->content;
				}
				else
				{
					// element is probably a form
					foreach ($this->children as $key => $input)
					{
						$this->content .= $input->compile();
					}
				}
			}
		}
		
		// Until now, we rendered the children, here we form_compile the element itself
		if (empty($this->printed))
		{
			$output = $this->_compile();
			$this->printed = TRUE;
		}
		
		// check for any HTML prefixes/suffixes
		$prefix = !empty($this->prefix) ? $this->prefix : '';
		$suffix = !empty($this->suffix) ? $this->suffix : '';
		$output = $prefix . $output . $suffix;
		
		return $output;
	}

	/**
	 * Enter description here ...
	 * 
	 * @return string
	 */
	abstract public function _compile();

	/**
	 * render()
	 * 
	 * Enter description here ...
	 * 
	 * @return string
	 */
	public function render($value)
	{
		$output = '<div class="form-item ' . html_class($this->type) . '"';
		if (!empty($this->id))
		{
			$output .= ' id="' . $this->id . '-wrapper"';
		}
		
		$output .= ">\n";
		
		$required = !empty($this->required) ? '<span class="form-required" title="This field is required.">*</span>' : '';
		
		if (!empty($this->title))
		{
			if (!empty($this->id))
			{
				$output .= ' <label for="' . $this->id . '">' . $this->title . ': ' . $required . "</label>\n";
			}
			else
			{
				$output .= ' <label>' . $this->title . ': ' . $required . "</label>\n";
			}
		}
		
		$output .= $value . "\n";
		
		if (!empty($this->description))
		{
			if (isset($this->type))
			{
				$output .= ' <div class="description small field-' . html_class($this->type) . '">' . $this->description . "</div>\n";
			}
			else
			{
				$output .= ' <div class="description small">' . $this->description . "</div>\n";
			}
		}
		
		$output .= "</div>\n";
		
		return $output;
	}

	/**
	 * addHtmlClasses()
	 * 
	 * Enter description here ...
	 * 
	 * @param unknown_type $classes
	 */
	public function addHtmlClasses($classes = array())
	{
		if (!empty($this->attributes['class']))
		{
			$classes[] = $this->attributes['class'];
		}
		
		if ($this->form->error($this->name) && $this->type != 'select')
		{
			$classes[] = 'error';
		}
		
		$this->attributes['class'] = implode(' ', $classes);
	}
}
